package core;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import core.modeling.metamodel.Classe;
import core.modeling.metamodel.Metodo;
import core.modeling.metamodel.Parametro;
import core.nodos.NoExpressao;

public class Contexto {
	private static boolean DEBUG = false;
	
	private String classeOuPacote;
	
	private boolean preCondicao;

	private Stack<NoExpressao> nos;
	private Stack<ContextoOperacao> operacoes;
	
	private Classe classeDoContexto;
	private Metodo metodoDoContexto;
	
	private List<Parametro> parametrosDoContexto;
	
	private List<String> estereotiposDoContexto;
	
	public Contexto() {
		this.nos = new Stack<NoExpressao>();
		this.operacoes = new Stack<ContextoOperacao>();
		this.parametrosDoContexto = new ArrayList<Parametro>();
		this.estereotiposDoContexto = new ArrayList<String>();
	}
	
	public boolean isDefinido() {
		return getContexto() != null;
	}
	
	public void setClasse(Classe classe) {
		this.classeDoContexto = classe;
	}
	
	public Classe getClasse() {
		return classeDoContexto;
	}
	
	public ContextoOperacao getOperacao() {
		if (operacoes.isEmpty())
			return null;
		
		return operacoes.peek();
	}
	
	public NoExpressao getContexto() {
		if (nos.isEmpty())
			return null;
		
		return nos.peek();
	}

	public int getPosicaoDoParam(Parametro p) {
		int i = 0;
		for (Parametro param : parametrosDoContexto) {
			if (param == p) {
				return i;
			}
			i++;
		}
		return -1;
	}

	public void trocaContexto(NoExpressao noCorrente) {
		debug("### TROCA ###");
		debug("STACK ANTES: " + nos);
		if (!nos.isEmpty())
			nos.pop();
		nos.push(noCorrente);
		debug("STACK DEPOIS: " + nos);
	}
	
	public void voltaContexto() {
		debug("### VOLTA ###");
		debug("STACK ANTES: " + nos);
		if (!nos.isEmpty())
			nos.pop();
		debug("STACK DEPOIS: " + nos);
	}

	public void adicionaNovoContexto() {
		debug("### ADD CONTEXTO ###");
		debug("STACK ANTES: " + nos);
		nos.push(null);
		debug("STACK DEPOIS: " + nos);
	}
	
	public void adicionaNovaOperacao(ContextoOperacao operacao) {
		operacoes.push(operacao);
	}
	
	public void removeUltimaOperacao() {
		operacoes.pop();
	}

	public void setMetodo(Metodo metodo) {
		this.metodoDoContexto = metodo;
	}
	
	public Metodo getMetodo() {
		return metodoDoContexto;
	}

	public void setPreCondicao(boolean preCondicao) {
		this.preCondicao = preCondicao;
	}

	public boolean isPreCondicao() {
		return preCondicao;
	}
	
	public void addParametro(String nomeUsado, Classe tipoParametro) {
		parametrosDoContexto.add(new Parametro(nomeUsado, tipoParametro));
	}

	public int getQuantidadeDeParametros() {
		return parametrosDoContexto.size();
	}

	public Parametro getParam(String nome) {
		for (Parametro p : parametrosDoContexto)
			if (p.getNome().equals(nome))
				return p;
		return null;
	}
	
	public void setVerificandoParametros(boolean verificandoParametros) {
		ContextoOperacao op = getOperacao();
		if (op != null)
			op.setVerificandoParametros(verificandoParametros);
	}
	
	public boolean isVerificandoParametros() {
		ContextoOperacao op = getOperacao();
		if (op != null)
			return op.isVerificandoParametros();
		return false;
	}
	
	public boolean isVerificandoParametrosDeColecao() {
		return isVerificandoParametros() && getOperacao() != null
				&& getOperacao().isOperacaoColecao();
	}
	
	public static void debug(String msg) {
		if (DEBUG) {
			System.out.println(msg);
		}
	}

	public void setClasseOuPacote(String classeOuPacote) {
		this.classeOuPacote = classeOuPacote;
	}

	public String getClasseOuPacote() {
		return classeOuPacote;
	}

	public void addEstereotipoNoContexto(String estereotipo) {
		estereotiposDoContexto.add(estereotipo);
	}
	
	public boolean possuiEstereotipo(String estereotipo) {
		for (String e : estereotiposDoContexto) {
			if (e.equals(estereotipo)) {
				return true;
			}
		}
		return false;
	}
	
	public void setEstereotiposDoContexto(List<String> estereotiposDoContexto) {
		this.estereotiposDoContexto = estereotiposDoContexto;
	}

	public List<String> getEstereotiposDoContexto() {
		return estereotiposDoContexto;
	}
}
